#include <wx/wxprec.h>
#include <wx/scrolbar.h>
#include <wx/menu.h>
#include <wx/timer.h>
#include <wx/clipbrd.h>
#include <wx/textbuf.h>
#include "wx/wxscintilla.h"
#include "ScintillaWX.h"
#include "PlatWX.h"
#include <wx/popupwin.h>
#include "wx/panel.h"
#include <wx/dcbuffer.h>

#ifdef SCI_LEXER
#include "LexerModule.h"
#include "ExternalLexer.h"
#endif

#define wxSCICallTipBase wxPopupWindow

#include "gtk/gtk.h"

#ifdef SCI_NAMESPACE
using namespace Scintilla;
#endif

class wxSCITimer : public wxTimer {
  public:
    wxSCITimer( ScintillaWX* swx ) {
      m_swx = swx;
    }
    void Notify() {
      m_swx->DoTick();
    }
  private:
    ScintillaWX* m_swx;
};


#if wxUSE_DRAG_AND_DROP
bool wxSCIDropTarget::OnDropText( wxCoord x, wxCoord y, const wxString& data ) {
  return m_swx->DoDropText( x, y, data );
}

wxDragResult  wxSCIDropTarget::OnEnter( wxCoord x, wxCoord y, wxDragResult def ) {
  return m_swx->DoDragEnter( x, y, def );
}

wxDragResult  wxSCIDropTarget::OnDragOver( wxCoord x, wxCoord y, wxDragResult def ) {
  return m_swx->DoDragOver( x, y, def );
}

void  wxSCIDropTarget::OnLeave() {
  m_swx->DoDragLeave();
}
#endif



class wxSCICallTipContent : public wxPanel {
  public:
    wxSCICallTipContent( wxWindow* parent, CallTip* ct, ScintillaWX* swx )
      : wxPanel( parent, -1 ),
        m_ct( ct ), m_swx( swx ) {
      SetBackgroundStyle( wxBG_STYLE_CUSTOM );
    }
    bool AcceptsFocus() const { return false; }
    void OnPaint( wxPaintEvent& ( evt ) ) {
      wxAutoBufferedPaintDC dc( this );
      Surface* surfaceWindow = Surface::Allocate( SC_TECHNOLOGY_DEFAULT );
      surfaceWindow->Init( &dc, m_ct->wDraw.GetID() );
      m_ct->PaintCT( surfaceWindow );
      surfaceWindow->Release();
      delete surfaceWindow;
    }
    void OnFocus( wxFocusEvent& event ) {
      GetGrandParent()->SetFocus();
      event.Skip();
    }
    void OnLeftDown( wxMouseEvent& event ) {
      wxPoint pt = event.GetPosition();
      Point p( pt.x, pt.y );
      m_ct->MouseClick( p );
      m_swx->CallTipClick();
    }
  private:
    CallTip* m_ct;
    ScintillaWX* m_swx;
    DECLARE_EVENT_TABLE()
};


BEGIN_EVENT_TABLE( wxSCICallTipContent, wxPanel )
  EVT_PAINT( wxSCICallTipContent::OnPaint )
  EVT_SET_FOCUS( wxSCICallTipContent::OnFocus )
  EVT_LEFT_DOWN( wxSCICallTipContent::OnLeftDown )
END_EVENT_TABLE()

class wxSCICallTip : public wxSCICallTipBase {
  public:
    wxSCICallTip( wxWindow* parent, CallTip* ct, ScintillaWX* swx ) :
      wxSCICallTipBase( parent, wxBORDER_NONE ),
      m_cx( wxDefaultCoord ), m_cy( wxDefaultCoord ) {
      SetBackgroundStyle( wxBG_STYLE_CUSTOM );
      m_content = new wxSCICallTipContent( this, ct, swx );
    }

    ~wxSCICallTip() {
      #if wxUSE_POPUPWIN && defined(__WXGTK__)
      wxRect rect = GetRect();
      rect.x = m_cx;
      rect.y = m_cy;
      GetParent()->Refresh( false, &rect );
      #endif
    }
    bool AcceptsFocus() const { return false; }
    virtual void DoSetSize( int x, int y, int width, int height, int sizeFlags = wxSIZE_AUTO ) {
      if( x != wxDefaultCoord ) {
        m_cx = x;
        GetParent()->ClientToScreen( &x, NULL );
      }
      if( y != wxDefaultCoord ) {
        m_cy = y;
        GetParent()->ClientToScreen( NULL, &y );
      }
      wxSCICallTipBase::DoSetSize( x, y, width, height, sizeFlags );
      m_content->SetSize( 0, 0, width, height, sizeFlags );
    }
    wxPoint GetMyPosition() {
      return wxPoint( m_cx, m_cy );
    }
  private:
    int m_cx, m_cy;
    wxSCICallTipContent* m_content;

};

#if wxUSE_DATAOBJ
static wxTextFileType wxConvertEOLMode( int scintillaMode ) {
  wxTextFileType type;
  switch( scintillaMode ) {
    case wxSCI_EOL_CRLF:
      type = wxTextFileType_Dos;
      break;
    case wxSCI_EOL_CR:
      type = wxTextFileType_Mac;
      break;
    case wxSCI_EOL_LF:
      type = wxTextFileType_Unix;
      break;
    default:
      type = wxTextBuffer::typeDefault;
      break;
  }
  return type;
}
#endif

static int wxCountLines( const char* text, int scintillaMode ) {
  char eolchar;
  switch( scintillaMode ) {
    case wxSCI_EOL_CRLF:
    case wxSCI_EOL_LF:
      eolchar = '\n';
      break;
    case wxSCI_EOL_CR:
      eolchar = '\r';
      break;
    default:
      return 0;
  }
  int count = 0;
  int i = 0;
  while( text[i] != 0 ) {
    if( text[i] == eolchar ) {
      count++;
    }
    i++;
  }
  return count;
}

const long int timerIDs[ScintillaWX::timersCount] = { wxNewId(), wxNewId(), wxNewId(), wxNewId() };

ScintillaWX::ScintillaWX( wxScintilla* win ) {
  capturedMouse = false;
  focusEvent = false;
  wMain = win;
  sci = win;
  wheelVRotation = 0;
  wheelHRotation = 0;
  Initialise();
  for( TickReason tr = tickCaret; tr <= tickDwell; tr = static_cast<TickReason>( tr + 1 ) ) {
    timers[tr] = new wxTimer( sci, timerIDs[( int )tr] );
    sci->Connect( timers[tr]->GetId(), wxEVT_TIMER, wxTimerEventHandler( wxScintilla::OnTimer ) );
  }
}

ScintillaWX::~ScintillaWX() {
  for( TickReason tr = tickCaret; tr <= tickDwell; tr = static_cast<TickReason>( tr + 1 ) ) {
    if( timers[tr] ) {
      sci->Disconnect( timers[tr]->GetId(), wxEVT_TIMER, wxTimerEventHandler( wxScintilla::OnTimer ) );
      timers[tr]->Stop();
      delete timers[tr];
    }
  }
  Finalise();
}

void ScintillaWX::Initialise() {
  #if wxUSE_DRAG_AND_DROP
  dropTarget = new wxSCIDropTarget;
  dropTarget->SetScintilla( this );
  sci->SetDropTarget( dropTarget );
  dragRectangle = false;
  #endif
  vs.extraFontFlag = true;
}

void ScintillaWX::Finalise() {
  ScintillaBase::Finalise();
  SetIdle( false );
  DestroySystemCaret();
}

void ScintillaWX::StartDrag() {
  #if wxUSE_DRAG_AND_DROP
  wxString dragText = sci2wx( drag.Data(), drag.Length() );
  wxScintillaEvent evt( wxEVT_SCI_START_DRAG, sci->GetId() );
  evt.SetEventObject( sci );
  evt.SetDragText( dragText );
  evt.SetDragFlags( wxDrag_DefaultMove );
  evt.SetPosition( wxMin( sci->GetSelectionStart(),
                          sci->GetSelectionEnd() ) );
  sci->GetEventHandler()->ProcessEvent( evt );
  UndoGroup ug( pdoc );
  dragText = evt.GetDragText();
  dragRectangle = drag.rectangular;
  if( !dragText.empty() ) {
    wxDropSource        source( sci );
    wxTextDataObject    data( dragText );
    wxDragResult        result;
    source.SetData( data );
    dropWentOutside = true;
    inDragDrop = ddDragging;
    result = source.DoDragDrop( evt.GetDragFlags() );
    if( result == wxDragMove && dropWentOutside ) {
      ClearSelection();
    }
    inDragDrop = ddNone;
    SetDragPosition( SelectionPosition( invalidPosition ) );
    wxScintillaEvent evtFin( wxEVT_SCI_FINISHED_DRAG, sci->GetId() );
    evtFin.SetEventObject( sci );
    sci->GetEventHandler()->ProcessEvent( evtFin );
  }
  #endif
}

bool ScintillaWX::SetIdle( bool on ) {
  if( idler.state != on ) {
    if( on ) {
      sci->Connect( wxID_ANY, wxEVT_IDLE, wxIdleEventHandler( wxScintilla::OnIdle ) );
    } else
    { sci->Disconnect( wxID_ANY, wxEVT_IDLE, wxIdleEventHandler( wxScintilla::OnIdle ) ); }
    idler.state = on;
  }
  return idler.state;
}

void ScintillaWX::SetMouseCapture( bool on ) {
  if( mouseDownCaptures ) {
    if( on && !capturedMouse ) {
      sci->CaptureMouse();
    } else if( !on && capturedMouse && sci->HasCapture() ) {
      sci->ReleaseMouse();
    }
    capturedMouse = on;
  }
}

bool ScintillaWX::HaveMouseCapture() {
  return capturedMouse;
}

void ScintillaWX::ScrollText( int linesToMove ) {
  int dy = vs.lineHeight * ( linesToMove );
  sci->ScrollWindow( 0, dy );
}

void ScintillaWX::SetVerticalScrollPos() {
  if( sci->m_vScrollBar == NULL ) {
    sci->SetScrollPos( wxVERTICAL, topLine );
  } else {
    sci->m_vScrollBar->SetThumbPosition( topLine );
  }
}

void ScintillaWX::SetHorizontalScrollPos() {
  if( sci->m_hScrollBar == NULL ) {
    sci->SetScrollPos( wxHORIZONTAL, xOffset );
  } else {
    sci->m_hScrollBar->SetThumbPosition( xOffset );
  }
}

const int H_SCROLL_STEP = 20;

bool ScintillaWX::ModifyScrollBars( int nMax, int nPage ) {
  bool modified = false;
  int vertEnd = nMax;
  if( !verticalScrollBarVisible ) {
    vertEnd = 0;
  }
  if( sci->m_vScrollBar == NULL ) {
    int sbMax = sci->GetScrollRange( wxVERTICAL );
    int sbThumb = sci->GetScrollThumb( wxVERTICAL );
    int sbPos = sci->GetScrollPos( wxVERTICAL );
    if( sbMax != vertEnd || sbThumb != nPage ) {
      sci->SetScrollbar( wxVERTICAL, sbPos, nPage, vertEnd + 1 );
      modified = true;
    }
  } else {
    int sbMax = sci->m_vScrollBar->GetRange();
    int sbPage = sci->m_vScrollBar->GetPageSize();
    int sbPos = sci->m_vScrollBar->GetThumbPosition();
    if( sbMax != vertEnd || sbPage != nPage ) {
      sci->m_vScrollBar->SetScrollbar( sbPos, nPage, vertEnd + 1, nPage );
      modified = true;
    }
  }
  PRectangle rcText = GetTextRectangle();
  int horizEnd = scrollWidth;
  if( horizEnd < 0 ) {
    horizEnd = 0;
  }
  if( !horizontalScrollBarVisible || Wrapping() ) {
    horizEnd = 0;
  }
  int pageWidth = rcText.Width();
  if( sci->m_hScrollBar == NULL ) {
    int sbMax = sci->GetScrollRange( wxHORIZONTAL );
    int sbThumb = sci->GetScrollThumb( wxHORIZONTAL );
    int sbPos = sci->GetScrollPos( wxHORIZONTAL );
    if( ( sbMax != horizEnd ) || ( sbThumb != pageWidth ) || ( sbPos != 0 ) ) {
      sci->SetScrollbar( wxHORIZONTAL, sbPos, pageWidth, horizEnd );
      modified = true;
      if( scrollWidth < pageWidth ) {
        HorizontalScrollTo( 0 );
      }
    }
  } else {
    int sbMax = sci->m_hScrollBar->GetRange();
    int sbThumb = sci->m_hScrollBar->GetPageSize();
    int sbPos = sci->m_hScrollBar->GetThumbPosition();
    if( ( sbMax != horizEnd ) || ( sbThumb != pageWidth ) || ( sbPos != 0 ) ) {
      sci->m_hScrollBar->SetScrollbar( sbPos, pageWidth, horizEnd, pageWidth );
      modified = true;
      if( scrollWidth < pageWidth ) {
        HorizontalScrollTo( 0 );
      }
    }
  }
  return modified;
}

void ScintillaWX::NotifyFocus( bool focus ) {
  sci->NotifyFocus( focus );
}

void ScintillaWX::NotifyChange() {
  sci->NotifyChange();
}

void ScintillaWX::NotifyParent( SCNotification scn ) {
  sci->NotifyParent( &scn );
}

bool ScintillaWX::FineTickerAvailable() {
  return true;
}

bool ScintillaWX::FineTickerRunning( TickReason reason ) {
  wxASSERT( static_cast<int>( reason ) >= 0 && static_cast<int>( reason ) < timersCount );
  wxASSERT( timers[reason] );
  return timers[reason]->IsRunning();
}

void ScintillaWX::FineTickerStart( TickReason reason, int millis, int ) {
  FineTickerCancel( reason );
  wxASSERT( static_cast<int>( reason ) >= 0 && static_cast<int>( reason ) < timersCount );
  wxASSERT( timers[reason] );
  timers[reason]->Start( millis );
}

void ScintillaWX::FineTickerCancel( TickReason reason ) {
  wxASSERT( static_cast<int>( reason ) >= 0 && static_cast<int>( reason ) < timersCount );
  wxASSERT( timers[reason] );
  timers[reason]->Stop();
}

void ScintillaWX::CancelModes() {
  if( ! focusEvent ) {
    AutoCompleteCancel();
  }
  ct.CallTipCancel();
  Editor::CancelModes();
}

void ScintillaWX::Copy() {
  if( !SelectionEmpty() ) {
    SelectionText st;
    CopySelectionRange( &st );
    CopyToClipboard( st );
  }
}

void ScintillaWX::Paste() {
  UndoGroup ug( pdoc );
  SelectionPosition firstPosition = SelectionStart();
  if( sel.IsRectangular() && !sel.Empty() ) {
    for( size_t i = 0; i < sel.Count() - 1; i++ ) {
      sel.RotateMain();
      if( firstPosition > SelectionStart() ) {
        firstPosition = SelectionStart();
      }
    }
  }
  ClearSelection( multiPasteMode == SC_MULTIPASTE_EACH );
  #if wxUSE_DATAOBJ
  wxTextDataObject data;
  wxString textString;
  bool isRectangularClipboard = false;
  wxTheClipboard->UsePrimarySelection( false );
  if( wxTheClipboard->Open() ) {
    wxDataFormat dataFormat( wxString( _T( "application/x-cbrectdata" ) ) );
    wxCustomDataObject selData( dataFormat );
    bool gotRectData = wxTheClipboard->GetData( selData );
    if( gotRectData && selData.GetSize() > 1 ) {
      wxTheClipboard->Close();
      const char* rectBuf = ( const char* )selData.GetData();
      isRectangularClipboard = rectBuf[0] == ( char )1;
      int len = selData.GetDataSize() - 1;
      char* buffer = new char[len];
      memcpy( buffer, rectBuf + 1, len );
      textString = sci2wx( buffer, len );
      delete [] buffer;
    } else {
      bool gotData = wxTheClipboard->GetData( data );
      wxTheClipboard->Close();
      if( gotData ) {
        textString = wxTextBuffer::Translate( data.GetText(),
                                              wxConvertEOLMode( pdoc->eolMode ) );
      }
    }
  }
  wxScintillaEvent evt( wxEVT_SCI_CLIPBOARD_PASTE, sci->GetId() );
  evt.SetEventObject( sci );
  evt.SetPosition( sel.MainCaret() );
  evt.SetString( textString );
  sci->GetEventHandler()->ProcessEvent( evt );
  wxWX2MBbuf buf = ( wxWX2MBbuf )wx2sci( textString );
  data.SetText( wxEmptyString );
  textString = wxEmptyString;
  int len  = strlen( buf );
  int newPos = 0;
  int caretMain = CurrentPosition();
  if( isRectangularClipboard ) {
    SelectionPosition selStart = sel.Range( sel.Main() ).Start();
    int newLine = pdoc->LineFromPosition( caretMain ) + wxCountLines( buf, pdoc->eolMode );
    int newCol = pdoc->GetColumn( caretMain );
    PasteRectangular( selStart, buf, len );
    newPos = pdoc->FindColumn( newLine, newCol );
  } else {
    pdoc->InsertString( caretMain, buf, len );
    newPos = caretMain + len;
  }
  SetEmptySelection( newPos );
  #endif
  NotifyChange();
  Redraw();
}

void ScintillaWX::CopyToClipboard( const SelectionText& st ) {
  #if wxUSE_CLIPBOARD
  if( !st.LengthWithTerminator() ) {
    return;
  }
  wxScintillaEvent evt( wxEVT_SCI_CLIPBOARD_COPY, sci->GetId() );
  evt.SetEventObject( sci );
  evt.SetString( wxTextBuffer::Translate( sci2wx( st.Data(), st.Length() ) ) );
  sci->GetEventHandler()->ProcessEvent( evt );
  wxTheClipboard->UsePrimarySelection( false );
  if( wxTheClipboard->Open() ) {
    wxString text = wxTextBuffer::Translate( sci2wx( st.Data(), st.Length() ) );
    wxDataObjectComposite* obj = new wxDataObjectComposite();
    wxCustomDataObject* rectData = new wxCustomDataObject( wxDataFormat( wxString( _T( "application/x-cbrectdata" ) ) ) );
    char* buffer = new char[st.LengthWithTerminator()];
    buffer[0] = ( st.rectangular ) ? ( char )1 : ( char )0;
    memcpy( buffer + 1, st.Data(), st.Length() );
    rectData->SetData( st.LengthWithTerminator(), buffer );
    delete [] buffer;
    obj->Add( rectData, true );
    obj->Add( new wxTextDataObject( text ) );
    wxTheClipboard->SetData( obj );
    wxTheClipboard->Close();
  }
  #else
  wxUnusedVar( st );
  #endif
}

bool ScintillaWX::CanPaste() {
  #if wxUSE_CLIPBOARD
  bool canPaste = false;
  bool didOpen;
  if( Editor::CanPaste() ) {
    wxTheClipboard->UsePrimarySelection( false );
    didOpen = !wxTheClipboard->IsOpened();
    if( didOpen ) {
      wxTheClipboard->Open();
    }
    if( wxTheClipboard->IsOpened() ) {
      canPaste = wxTheClipboard->IsSupported( wxUSE_UNICODE ? wxDF_UNICODETEXT : wxDF_TEXT );
      if( didOpen ) {
        wxTheClipboard->Close();
      }
    }
  }
  return canPaste;
  #else
  return false;
  #endif
}

void ScintillaWX::CreateCallTipWindow( PRectangle ) {
  if( ! ct.wCallTip.Created() ) {
    ct.wCallTip = new wxSCICallTip( sci, &ct, this );
    ct.wDraw = ct.wCallTip;
  }
}

void ScintillaWX::AddToPopUp( const char *label, int cmd, bool enabled ) {
  if( !label[0] ) {
    ( ( wxMenu* )popup.GetID() )->AppendSeparator();
  } else {
    ( ( wxMenu* )popup.GetID() )->Append( cmd, wxGetTranslation( sci2wx( label ) ) );
  }
  if( !enabled ) {
    ( ( wxMenu* )popup.GetID() )->Enable( cmd, enabled );
  }
}

void ScintillaWX::ClaimSelection() {
  if( !SelectionEmpty() ) {
    SelectionText st;
    CopySelectionRange( &st );
  }
}

void ScintillaWX::UpdateSystemCaret() {
}

bool ScintillaWX::HasCaretSizeChanged() {
  return false;
}

bool ScintillaWX::CreateSystemCaret() {
  return false;
}

bool ScintillaWX::DestroySystemCaret() {
  return false;
}

sptr_t ScintillaWX::DefWndProc( unsigned int, uptr_t, sptr_t ) {
  return 0;
}

sptr_t ScintillaWX::WndProc( unsigned int iMessage, uptr_t wParam, sptr_t lParam ) {
  switch( iMessage ) {
    case SCI_CALLTIPSHOW: {
      Point pt = LocationFromPosition( wParam );
      char* defn = reinterpret_cast<char *>( lParam );
      AutoCompleteCancel();
      int ctStyle = ct.UseStyleCallTip() ? STYLE_CALLTIP : STYLE_DEFAULT;
      if( ct.UseStyleCallTip() ) {
        ct.SetForeBack( vs.styles[STYLE_CALLTIP].fore, vs.styles[STYLE_CALLTIP].back );
      }
      int caretMain = sel.MainCaret();
      PRectangle rc = ct.CallTipStart( caretMain, pt, vs.lineHeight, defn, vs.styles[ctStyle].fontName,
                                       vs.styles[ctStyle].sizeZoomed, CodePage(), vs.styles[ctStyle].characterSet,
                                       vs.technology, wMain );
      PRectangle rcClient = GetClientRectangle();
      if( rc.bottom > rcClient.bottom ) {
        int offset = int( vs.lineHeight * 1.25 )  + rc.Height();
        rc.top -= offset;
        rc.bottom -= offset;
      }
      if( rc.right > rcClient.right ) {
        rc.left -= rc.right - rcClient.right;
        rc.right = rcClient.right;
      }
      CreateCallTipWindow( rc );
      ct.wCallTip.SetPositionRelative( rc, wMain );
      ct.wCallTip.Show();
      break;
    }
    case SCI_GETDIRECTFUNCTION:
      return reinterpret_cast<sptr_t>( DirectFunction );
    case SCI_GETDIRECTPOINTER:
      return reinterpret_cast<sptr_t>( this );
    case SCI_GRABFOCUS:
      sci->SetFocus();
      break;
      #ifdef SCI_LEXER
    case SCI_LOADLEXERLIBRARY:
      LexerManager::GetInstance()->Load( ( const char* )lParam );
      break;
      #endif
    default:
      return ScintillaBase::WndProc( iMessage, wParam, lParam );
  }
  return 0;
}

sptr_t ScintillaWX::DirectFunction( ScintillaWX *wxsci, unsigned int iMessage, uptr_t wParam, sptr_t lParam ) {
  return wxsci->WndProc( iMessage, wParam, lParam );
}

void ScintillaWX::DoPaint( wxDC* dc, wxRect rect ) {
  paintState = painting;
  AutoSurface surfaceWindow( dc, this );
  if( surfaceWindow ) {
    rcPaint = PRectangleFromwxRect( rect );
    PRectangle rcClient = GetClientRectangle();
    paintingAllText = rcPaint.Contains( rcClient );
    ClipChildren( *dc, rcPaint );
    Paint( surfaceWindow, rcPaint );
    surfaceWindow->Release();
  }
  if( paintState == paintAbandoned ) {
    sci->Refresh( false );
    #if defined(__WXOSX__)
    FullPaintDC( dc );
    #endif
  }
  paintState = notPainting;
}

void ScintillaWX::FullPaint() {
  sci->Refresh( false );
  sci->Update();
}

void ScintillaWX::FullPaintDC( wxDC* dc ) {
  paintState = painting;
  rcPaint = GetClientRectangle();
  paintingAllText = true;
  AutoSurface surfaceWindow( dc, this );
  if( surfaceWindow ) {
    Paint( surfaceWindow, rcPaint );
    surfaceWindow->Release();
  }
  paintState = notPainting;
}

void ScintillaWX::DoHScroll( int type, int pos ) {
  int xPos = xOffset;
  PRectangle rcText = GetTextRectangle();
  int pageWidth = rcText.Width() * 2 / 3;
  if( type == wxEVT_SCROLLWIN_LINEUP || type == wxEVT_SCROLL_LINEUP ) {
    xPos -= H_SCROLL_STEP;
  } else if( type == wxEVT_SCROLLWIN_LINEDOWN || type == wxEVT_SCROLL_LINEDOWN ) {
    xPos += H_SCROLL_STEP;
  } else if( type == wxEVT_SCROLLWIN_PAGEUP || type == wxEVT_SCROLL_PAGEUP ) {
    xPos -= pageWidth;
  } else if( type == wxEVT_SCROLLWIN_PAGEDOWN || type == wxEVT_SCROLL_PAGEDOWN ) {
    xPos += pageWidth;
    if( xPos > scrollWidth - rcText.Width() ) {
      xPos = scrollWidth - rcText.Width();
    }
  } else if( type == wxEVT_SCROLLWIN_TOP || type == wxEVT_SCROLL_TOP ) {
    xPos = 0;
  } else if( type == wxEVT_SCROLLWIN_BOTTOM || type == wxEVT_SCROLL_BOTTOM ) {
    xPos = scrollWidth;
  } else if( type == wxEVT_SCROLLWIN_THUMBTRACK || type == wxEVT_SCROLL_THUMBTRACK ) {
    xPos = pos;
  }
  HorizontalScrollTo( xPos );
}

void ScintillaWX::DoVScroll( int type, int pos ) {
  int topLineNew = topLine;
  if( type == wxEVT_SCROLLWIN_LINEUP || type == wxEVT_SCROLL_LINEUP ) {
    topLineNew -= 1;
  } else if( type == wxEVT_SCROLLWIN_LINEDOWN || type == wxEVT_SCROLL_LINEDOWN ) {
    topLineNew += 1;
  } else if( type ==  wxEVT_SCROLLWIN_PAGEUP || type == wxEVT_SCROLL_PAGEUP ) {
    topLineNew -= LinesToScroll();
  } else if( type ==  wxEVT_SCROLLWIN_PAGEDOWN || type == wxEVT_SCROLL_PAGEDOWN ) {
    topLineNew += LinesToScroll();
  } else if( type ==  wxEVT_SCROLLWIN_TOP || type == wxEVT_SCROLL_TOP ) {
    topLineNew = 0;
  } else if( type ==  wxEVT_SCROLLWIN_BOTTOM || type == wxEVT_SCROLL_BOTTOM ) {
    topLineNew = MaxScrollPos();
  } else if( type ==   wxEVT_SCROLLWIN_THUMBTRACK || type == wxEVT_SCROLL_THUMBTRACK ) {
    topLineNew = pos;
  }
  ScrollTo( topLineNew );
}

void ScintillaWX::DoMouseWheel( wxMouseWheelAxis axis, int rotation, int delta, int linesPerAction, int columnsPerAction,
                                bool ctrlDown, bool isPageScroll ) {
  int topLineNew = topLine;
  int lines;
  int xPos = xOffset;
  int pixels;
  if( axis == wxMOUSE_WHEEL_HORIZONTAL ) {
    wheelHRotation += rotation * ( columnsPerAction * vs.spaceWidth );
    pixels = wheelHRotation / delta;
    wheelHRotation -= pixels * delta;
    if( pixels != 0 ) {
      xPos += pixels;
      PRectangle rcText = GetTextRectangle();
      if( xPos > scrollWidth - rcText.Width() ) {
        xPos = scrollWidth - rcText.Width();
      }
      HorizontalScrollTo( xPos );
    }
  } else if( ctrlDown ) {
    if( rotation > 0 ) {
      KeyCommand( SCI_ZOOMIN );
    } else
    { KeyCommand( SCI_ZOOMOUT ); }
  } else {
    if( !delta ) {
      delta = 120;
    }
    wheelVRotation += rotation;
    lines = wheelVRotation / delta;
    wheelVRotation -= lines * delta;
    if( lines != 0 ) {
      if( isPageScroll ) {
        lines = lines * LinesOnScreen();
      } else
      { lines *= linesPerAction; }
      topLineNew -= lines;
      ScrollTo( topLineNew );
    }
  }
}

void ScintillaWX::DoSize( int ( width ), int ( height ) ) {
  ChangeSize();
}

void ScintillaWX::DoLoseFocus() {
  focusEvent = true;
  SetFocusState( false );
  focusEvent = false;
  SetMouseCapture( false );
  DestroySystemCaret();
}

void ScintillaWX::DoGainFocus() {
  focusEvent = true;
  SetFocusState( true );
  focusEvent = false;
  DestroySystemCaret();
  CreateSystemCaret();
}

void ScintillaWX::DoSysColourChange() {
  InvalidateStyleData();
}

void ScintillaWX::DoLeftButtonDown( Point pt, unsigned int curTime, bool shift, bool ctrl, bool alt ) {
  ButtonDown( pt, curTime, shift, ctrl, alt );
}

void ScintillaWX::DoLeftButtonUp( Point pt, unsigned int curTime, bool ctrl ) {
  ButtonUp( pt, curTime, ctrl );
}

void ScintillaWX::DoLeftButtonMove( Point pt ) {
  ButtonMove( pt );
}

void ScintillaWX::DoMiddleButtonUp( Point pt ) {
  int newPos = PositionFromLocation( pt );
  MovePositionTo( newPos, Selection::noSel, true );
  UndoGroup ug( pdoc );
  wxTextDataObject data;
  bool gotData = false;
  wxTheClipboard->UsePrimarySelection( true );
  if( wxTheClipboard->Open() ) {
    gotData = wxTheClipboard->GetData( data );
    wxTheClipboard->Close();
  }
  wxTheClipboard->UsePrimarySelection( false );
  if( gotData ) {
    wxString  text = wxTextBuffer::Translate( data.GetText(), wxConvertEOLMode( pdoc->eolMode ) );
    wxWX2MBbuf buf = ( wxWX2MBbuf )wx2sci( text );
    int len = strlen( buf );
    int caretMain = sel.MainCaret();
    pdoc->InsertString( caretMain, buf, len );
    SetEmptySelection( caretMain + len );
  }
  NotifyChange();
  Redraw();
  ShowCaretAtCurrentPosition();
  EnsureCaretVisible();
}

void ScintillaWX::DoAddChar( int key ) {
  wxChar wszChars[2];
  wszChars[0] = ( wxChar )key;
  wszChars[1] = 0;
  wxWX2MBbuf buf = ( wxWX2MBbuf )wx2sci( wszChars );
  AddCharUTF( ( char* )buf.data(), strlen( buf ) );
}

int  ScintillaWX::DoKeyDown( const wxKeyEvent& evt, bool* consumed ) {
  int key = evt.GetKeyCode();
  bool shift = evt.ShiftDown(),
       ctrl = evt.ControlDown(),
       alt = evt.AltDown();
  if( ctrl && key >= 1 && key <= 26 && key != WXK_BACK ) {
    key += 'A' - 1;
  }
  switch( key ) {
    case WXK_DOWN:
    case WXK_NUMPAD_DOWN:
      key = SCK_DOWN;
      break;
    case WXK_UP:
    case WXK_NUMPAD_UP:
      key = SCK_UP;
      break;
    case WXK_LEFT:
    case WXK_NUMPAD_LEFT:
      key = SCK_LEFT;
      break;
    case WXK_RIGHT:
    case WXK_NUMPAD_RIGHT:
      key = SCK_RIGHT;
      break;
    case WXK_HOME:
    case WXK_NUMPAD_HOME:
      key = SCK_HOME;
      break;
    case WXK_END:
    case WXK_NUMPAD_END:
      key = SCK_END;
      break;
    case WXK_PAGEUP:
    case WXK_NUMPAD_PAGEUP:
      key = SCK_PRIOR;
      break;
    case WXK_PAGEDOWN:
    case WXK_NUMPAD_PAGEDOWN:
      key = SCK_NEXT;
      break;
    case WXK_NUMPAD_DELETE:
    case WXK_DELETE:
      key = SCK_DELETE;
      break;
    case WXK_NUMPAD_INSERT:
    case WXK_INSERT:
      key = SCK_INSERT;
      break;
    case WXK_ESCAPE:
      key = SCK_ESCAPE;
      break;
    case WXK_BACK:
      key = SCK_BACK;
      break;
    case WXK_TAB:
      key = SCK_TAB;
      break;
    case WXK_NUMPAD_ENTER:
    case WXK_RETURN:
      key = SCK_RETURN;
      break;
    case WXK_ADD:
    case WXK_NUMPAD_ADD:
      key = SCK_ADD;
      break;
    case WXK_SUBTRACT:
    case WXK_NUMPAD_SUBTRACT:
      key = SCK_SUBTRACT;
      break;
    case WXK_DIVIDE:
    case WXK_NUMPAD_DIVIDE:
      key = SCK_DIVIDE;
      break;
    case WXK_CONTROL:
      key = 0;
      break;
    case WXK_ALT:
      key = 0;
      break;
    case WXK_SHIFT:
      key = 0;
      break;
    case WXK_MENU:
      key = SCK_MENU;
      break;
  }
  int rv = KeyDown( key, shift, ctrl, alt, consumed );
  if( key ) {
    return rv;
  } else {
    return 1;
  }
}

void ScintillaWX::DoCommand( int ID ) {
  Command( ID );
}

void ScintillaWX::DoContextMenu( Point pt ) {
  if( displayPopupMenu ) {
    if( HaveMouseCapture() ) {
      SetMouseCapture( false );
    }
    ContextMenu( pt );
  }
}

void ScintillaWX::DoOnListBox() {
  AutoCompleteCompleted();
}

void ScintillaWX::DoOnIdle( wxIdleEvent& evt ) {
  if( Idle() ) {
    evt.RequestMore();
  } else {
    SetIdle( false );
  }
}

void ScintillaWX::DoOnTimer( wxTimerEvent& evt ) {
  for( TickReason tr = tickCaret; tr <= tickDwell; tr = static_cast<TickReason>( tr + 1 ) ) {
    if( timers[tr] && timers[tr]->GetId() == evt.GetId() ) {
      TickFor( tr );
    }
  }
}

#if wxUSE_DRAG_AND_DROP
bool ScintillaWX::DoDropText( long x, long y, const wxString& data ) {
  SetDragPosition( SelectionPosition( invalidPosition ) );
  wxString text = wxTextBuffer::Translate( data,
                  wxConvertEOLMode( pdoc->eolMode ) );
  wxScintillaEvent evt( wxEVT_SCI_DO_DROP, sci->GetId() );
  evt.SetEventObject( sci );
  evt.SetDragResult( dragResult );
  evt.SetX( x );
  evt.SetY( y );
  evt.SetPosition( PositionFromLocation( Point( x, y ) ) );
  evt.SetDragText( text );
  sci->GetEventHandler()->ProcessEvent( evt );
  dragResult = evt.GetDragResult();
  if( dragResult == wxDragMove || dragResult == wxDragCopy ) {
    DropAt( SelectionPosition( evt.GetPosition() ),
            wx2sci( evt.GetDragText() ),
            dragResult == wxDragMove,
            dragRectangle );
    return true;
  }
  return false;
}


wxDragResult ScintillaWX::DoDragEnter( wxCoord ( x ), wxCoord ( y ), wxDragResult def ) {
  dragResult = def;
  return dragResult;
}


wxDragResult ScintillaWX::DoDragOver( wxCoord x, wxCoord y, wxDragResult def ) {
  SetDragPosition( SelectionPosition( PositionFromLocation( Point( x, y ) ) ) );
  wxScintillaEvent evt( wxEVT_SCI_DRAG_OVER, sci->GetId() );
  evt.SetEventObject( sci );
  evt.SetDragResult( def );
  evt.SetX( x );
  evt.SetY( y );
  evt.SetPosition( PositionFromLocation( Point( x, y ) ) );
  sci->GetEventHandler()->ProcessEvent( evt );
  dragResult = evt.GetDragResult();
  return dragResult;
}


void ScintillaWX::DoDragLeave() {
  SetDragPosition( SelectionPosition( invalidPosition ) );
}
#endif

void ScintillaWX::DoScrollToLine( int line ) {
  ScrollTo( line );
}

void ScintillaWX::DoScrollToColumn( int column ) {
  HorizontalScrollTo( column * vs.spaceWidth );
}

void ScintillaWX::ClipChildren( wxDC& ( dc ), PRectangle ( rect ) ) {
}

void ScintillaWX::SetUseAntiAliasing( bool useAA ) {
  vs.extraFontFlag = useAA;
  InvalidateStyleRedraw();
}

bool ScintillaWX::GetUseAntiAliasing() {
  return vs.extraFontFlag != 0;
}
